package psp.ex2b;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Locale;


/**
 * Class Exercise2B, which reads and writes real number in file 
 * @author josep
 *
 */
public class Exercise2B {

	private String fileName;
	private int option;
	private static final int READ = 0;
	private static final int WRITE = 1;
	private static final int MODIFY = 2;
	private LinkedList<Float> numbers = null; // Variable used to store the values read from the file 
	
	/**
	 * According to the option that the user chose, an action is performed
	 */
	void executeAction() {

		switch (this.option) {
			case READ: readNumbersFromTheFileAndDisplay(); break;
			case WRITE: writeNumbersInTheFile(); break;
			case MODIFY: modifyNumbersInTheFile(); break;
		}
	}
	
	/**
	 * Read the numbers from the file and display them
	 */
	private void readNumbersFromTheFileAndDisplay() {
		
		this.readNumbersFromFile();
		this.displayNumbersFromFile();
	}
	
	
	/**
	 * Display the numbers read in the file
	 */
	private void displayNumbersFromFile() {
		System.out.println("Numbers in the file: ");
		
		for (float number: this.numbers)
			this.displayCurrentNumber(number);
		
		System.out.println("All numbers displayed!");
	}
	
	
	/**
	 * Write integer and float numbers in a file
	 */
	private void writeNumbersInTheFile() {
		
		try {
			
			// open the output file
			PrintWriter out = new PrintWriter(this.fileName);

			String line = null;
			
		    try {
		    	BufferedReader is = new BufferedReader(
		    		new InputStreamReader(System.in));
		      
			    // read how many numbers the user want to store in the file
		    	System.out.println("Enter the number of numbers you want to store in the file: ");
			    line = is.readLine();
			    int numbersCount = Integer.parseInt(line);
			      
			    Float number;
			    
			    // Read the numbers from the console and write them in the file
			    System.out.println("Enter each number: ");
			    for (int i = 0; i < numbersCount; i++) {
			    	line = is.readLine();
				    number = Float.parseFloat(line);
				    out.printf(Locale.ENGLISH, "%.6g\n", number);
			    }
			    
				System.out.println("File created with the numbers you included!");
				out.close();

		    } catch (NumberFormatException ex) {
		      System.err.println("Not a valid number: " + line);
		    } catch (IOException e) {
		      System.err.println("Unexpected IO ERROR: " + e);
		    }
		}
		catch (IOException e)
		{
			System.out.println("IOException : " + e);
		}
	}
	
	/**
	 * Read a string with the file name from the console
	 */
	void readFileName() {
		try {
			InputStreamReader isr = new InputStreamReader(System.in);
			BufferedReader br = new BufferedReader(isr);
			System.out.println("Enter the file name : ");
			this.fileName = br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	
	/**
	 * Read an integer with the option that the user chose
	 */
	void readOption() {
		
		String line = null;
	    try {
	      BufferedReader is = new BufferedReader(
	        new InputStreamReader(System.in));
	      
	      System.out.println(
	    		  "What would you like to do with this file? Read, write or modify the file? ");
	      System.out.println("Please enter ");
	      System.out.println("-0 for reading the file");
	      System.out.println("-1 for writing a new file");
	      System.out.println("-2 for modifying the file ");
	      line = is.readLine();
	      this.option = Integer.parseInt(line);
	      
	      while (this.option != READ && this.option != WRITE && this.option != MODIFY) {
	    	  System.out.println(
	    			  "Invalid option! Please enter 0 for read option, " +
	    			  "1 for write option and 2 for modify option: ");
	    	  line = is.readLine();
	    	  this.option = Integer.parseInt(line);
	      }

	    } catch (NumberFormatException ex) {
	      System.err.println("Not a valid number: " + line);
	    } catch (IOException e) {
	      System.err.println("Unexpected IO ERROR: " + e);
	    }

	}
	
	/**
	 * Perform all actions that can be done in the modify option
	 */
	private void modifyNumbersInTheFile() {
		this.readNumbersFromFile();
		
		ModifyOptions currentOption = ModifyOptions.ACCEPT;
		ListIterator<Float> iterator = this.numbers.listIterator();
		while (currentOption != ModifyOptions.FINISH_READING && iterator.hasNext()) {
			
			System.out.println();
			System.out.print("The current number is: ");
			this.displayCurrentNumber(iterator.next());
			System.out.println();

			this.displayOptionsInModifyAction();
			
			currentOption = this.readModifyOption();
			
			switch (currentOption) {
			case ACCEPT : continue; 
			case REPLACE: 
				System.out.println("Please enter the number to replace: ");
				iterator.set(this.readFloatNumber());
				break;
			case DELETE:
				iterator.remove();
				break;
			case INSERT:
				System.out.println("Please enter the number to be added: ");
				iterator.add(this.readFloatNumber()); 
				break;
			case FINISH_READING:
				break;
			}
		}

		this.saveFile();
	}
	
	/**
	 * Read a number and returns it
	 * @return number read
	 */
	private float readFloatNumber() {
		String line = null;
		BufferedReader is = null;
	    try {
	      is = new BufferedReader(
	        new InputStreamReader(System.in));
	      line = is.readLine();
	      return Float.parseFloat(line);

	    } catch (NumberFormatException ex) {
	      System.err.println("Not a valid number: " + line);
	    }
	    catch (IOException e) {
		      System.err.println("Unexpected IO ERROR: " + e);
		}
	    return 0;
	}
	
	/**
	 * Show the options the user can choose (accept, replace, delete, insert another, finish reading the file
	 */
	private void displayOptionsInModifyAction() {
		System.out.println("What would you like to do with this number?");
		System.out.println("0 - Accept the number and go to the next one");
		System.out.println("1 - Replace the current number");
		System.out.println("2 - Delete the current number from the file");
		System.out.println("3 - Insert another number after this number");
		System.out.println("4 - Finish checking the number in the file and " +
				"accept the remaining ones");
		System.out.println("Option: ");
	}
	
	/**
	 * Displays the current number
	 * @param currentNumber number to be displayed
	 */
	private void displayCurrentNumber(float currentNumber) {
		System.out.println(currentNumber);
	}
	
	/**
	 * User should enter an option to perform an action
	 * @return Option the user chose
	 */
	private ModifyOptions readModifyOption() {
		String line = null;
		BufferedReader is = null;
		try {
	      is = new BufferedReader(
	        new InputStreamReader(System.in));
	      line = is.readLine();
	      int currentOption = Integer.parseInt(line);
	      
	      while (currentOption < 0 && currentOption > 4) {
	    	  System.out.println("Please enter a valid number!");
	    	  this.displayOptionsInModifyAction();
	    	  line = is.readLine();
		      currentOption = Integer.parseInt(line);
	      }
	      
	      switch (currentOption) {
	      case 0: return ModifyOptions.ACCEPT;
	      case 1: return ModifyOptions.REPLACE;
	      case 2: return ModifyOptions.DELETE;
	      case 3: return ModifyOptions.INSERT;
	      default: return ModifyOptions.FINISH_READING;
	      }

	    } catch (NumberFormatException ex) {
	      System.err.println("Not a valid number: " + line);
	    }
	    catch (IOException e) {
		      System.err.println("Unexpected IO ERROR: " + e);
		}
	    return null; 
	}
	
	/**
	 * Read numbers from file
	 */
	private void readNumbersFromFile() {
		
		// Check if the file exist
		if (!this.fileExists()) {
			System.out.println("The file entered does not exist!");
			System.exit(0);
		}
		
		try {
			FileInputStream fin = new FileInputStream(this.fileName);
			DataInputStream din = new DataInputStream(fin);
			
			float number = 0.0f;
			
			String line = null;
			this.numbers = new LinkedList<Float>();
			
			while (( line = din.readLine()) != null) {
				number = Float.parseFloat(line);
				this.numbers.add(number);
			}
			
			din.close();
		} catch (NumberFormatException ex) {
		    System.err.println("The file does not contain a valida real number!");
		}
		catch (FileNotFoundException ex) {
			System.err.println("The file entered is not valid!");
		}
		catch (IOException ex){
		    ex.printStackTrace();
	    }

	}
	
	/**
	 * Save a file with the new values after the user has changed
	 */
	private void saveFile() {
		int option = this.readOptionToSave();
		switch (option) {
		case -1: 
			System.out.println("A problem has happend while reading the option to save the file!"); 
			break;
		case 1:
			this.readFileName(); // It is not necessary to have a break command here because 
								 // I intend to save the file also, command in the next case clause.
		case 0:
			this.saveNewValuesInFile();
			break;
		}
	}
	
	/**
	 * Save the new values in specific file 
	 */
	private void saveNewValuesInFile() {
		try {

			// open the output file
			PrintWriter out = new PrintWriter(this.fileName);

		    ListIterator<Float> iterator = this.numbers.listIterator();
		    
		    while (iterator.hasNext())
			    out.printf(Locale.ENGLISH, "%.6g\n", iterator.next());

		    out.close();
			System.out.println("File created with the numbers modified!");
		}
		catch (IOException e)
		{
			System.out.println("IOException : " + e);
		}
	}
	
	/**
	 * Read the option the user want to save (change the original file or create a new one)
	 * @return option chosen
	 */
	private int readOptionToSave() {
		this.displayOptionsToSaveFile();
		
		String line = null;
		BufferedReader is = null;
		try {
	      is = new BufferedReader(
	        new InputStreamReader(System.in));
	      line = is.readLine();
	      int value = Integer.parseInt(line);
	      
	      while (value != 0 && value != 1) {
	    	  System.out.println("Please enter one of the valid options!");
	    	  this.displayOptionsToSaveFile();
	    	  line = is.readLine();
		      value = Integer.parseInt(line);
	      }
	      
	      return value;

	    } catch (NumberFormatException ex) {
	      System.err.println("Not a valid number: " + line);
	    }
	    catch (IOException e) {
		      System.err.println("Unexpected IO ERROR: " + e);
		}
	    
	    return -1;
	}
	
	/**
	 * Show the options the user can choose when saving the file
	 */
	private void displayOptionsToSaveFile() {
		System.out.println("What would you like to do?");
		System.out.println("0 - Save the values in the original file");
		System.out.println("1 - Save the values in a new file");
	}
	
	/**
	 * Check if a file exists
	 * @return true if exists, false otherwise
	 */
	private boolean fileExists() {
		File file = new File(this.fileName);
		return file.exists();
	}
}
